package com.qf.edu.address;

import com.qf.edu.tools.Base58;
import com.qf.edu.tools.Hash;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.time.Year;
import java.util.Arrays;

/**
 * @author: 吾忆
 * @email: 2432605231@qq.com
 * @phone:13677982829
 * @desc:该类用于推导生成比特币原始格式地址，并执行校验
 * @DateTime: 2021/12/26 0:29
 **/

public class Address {
    /**
     * 该方法用于生成一对密钥对，并将密钥对返回
     * @return  生成的密钥对
     */

    public KeyPair generateKey(){

        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
            //secp256k1、secp256r1

            ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
            generator.initialize(spec);
            return generator.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 该方法用于根据提供的keypair密钥信息，推导原始格式地址
     * @param keyPair  密钥信息
     * @return  生成的原始格式地址
     */

    public String newAddress(KeyPair keyPair){
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
       //自己进行原始格式公钥的拼接
        byte[] pubkey = toUnCompressPubKey(publicKey);

        //对公钥进行hash计算
        byte[] sha256 = Hash.SHA256(pubkey);
        //ripemd160
        byte[] pubhash = Hash.RipeMD160(sha256);

        //版本号
        byte[] version = new byte[1];
        version[0] = 0x00;
        //添加版本号
        byte[] ver_pubhash = new byte[version.length + pubhash.length];
        System.arraycopy(version,0,ver_pubhash,0,version.length);
        System.arraycopy(pubhash,0,ver_pubhash,1,pubhash.length);

        //双hash，取前4个字节
        byte[] hash1= Hash.SHA256(ver_pubhash);
        byte[] hash2= Hash.SHA256(hash1);

        //校验位数组
        byte[] check = new byte[4];
        System.arraycopy(hash2,0,check,0,check.length);

        //申请 ver_pubhash_check 数组
        byte[] ver_pubhash_check =new byte[ver_pubhash.length + check.length];
        System.arraycopy(ver_pubhash,0,ver_pubhash_check,0,ver_pubhash.length);
        //0 1 2 3 4 5 6 7
        //a b c
        System.arraycopy(check,0,ver_pubhash_check,ver_pubhash.length,check.length);

        // base58编码
        return  Base58.encode(ver_pubhash_check);

    }

    /**
     * 用于校验地址是否符合规范，并返回校验结果
     * @param addr  要检验的地址
     * @return  校验结果 true表示符合规范，false表示不符合规范
     */
    public boolean isValidate(String addr){
        if ("".equals(addr) || addr == null) {
            return false;
        }
        //1、base58解码
        byte[] ver_pubhash_check = Base58.decode(addr);
        if (ver_pubhash_check.length <4){
            return false;
        }

        //2、申请存储校验位的数组空间
        byte[] check = new byte[4];
        System.arraycopy(ver_pubhash_check,ver_pubhash_check.length-4,check,0,check.length);

        byte[] ver_pubhash = new byte[ver_pubhash_check.length-check.length];
        System.arraycopy(ver_pubhash_check,0,ver_pubhash,0,ver_pubhash.length);

        //双hash

        byte[] hash1 = Hash.SHA256(ver_pubhash);
        byte[] hash2 = Hash.SHA256(hash1);

        byte[] check_sum = new byte[4];
        System.arraycopy(hash2,0,check_sum,0,check_sum.length);

       return Arrays.equals(check,check_sum);

    }

    /**
     * 该方法用于拼接得到原始格式的公钥
     * @return  原始公钥字节数组
     */
    public byte[] toUnCompressPubKey(ECPublicKey publicKey){
        ECPoint point = publicKey.getW();
        byte[] xBytes = point.getAffineX().toByteArray();
        byte[] yBytes = point.getAffineY().toByteArray();

        //申请65字节大小的字节数组
        //告诉你：04 + x坐标（32） +y 坐标（32）
        byte[] pubKey = new byte[1+2*32];
        pubKey[0] = 04 ;


        //x坐标
        if (xBytes.length == 32 ){
             System.arraycopy(xBytes,0,pubKey,1,xBytes.length);
        }else if (xBytes.length > 32){
            System.arraycopy(xBytes,1,pubKey,1,32);
        }
        //y坐标
        if (yBytes.length == 32) {
            System.arraycopy(yBytes,0,pubKey,33,yBytes.length);
        }else if(yBytes.length > 32){
            System.arraycopy(yBytes,1,pubKey,33,32);
        }
        //将65字节的数据返回
        return pubKey;
    }

}
